import requests
import datetime
import hashlib
import uuid
import hmac
import base64

# 请求Header Accept
HTTP_HEADER_ACCEPT = 'Accept'
# 请求Body内容MD5 Header
HTTP_HEADER_CONTENT_MD5 = 'Content-MD5'
# 请求Header Content-Type
HTTP_HEADER_CONTENT_TYPE = 'Content-Type'
# 请求Header UserAgent
HTTP_HEADER_USER_AGENT = 'User-Agent'
# 请求Header Date
HTTP_HEADER_DATE = 'Date'
# 换行符
LF = '\n'
# 串联符
SPE1 = ','
# 示意符
SPE2 = ':'
# 连接符
SPE3 = '&'
# 赋值符
SPE4 = '='
# 问号符
SPE5 = '?'
# 参与签名的系统Header前缀,只有指定前缀的Header才会参与到签名中
CA_HEADER_TO_SIGN_PREFIX_SYSTEM = 'X-Ca-'
# 签名Header
X_CA_SIGNATURE = 'X-Ca-Signature'
# 所有参与签名的Header
X_CA_SIGNATURE_HEADERS = 'X-Ca-Signature-Headers'
# 请求时间戳
X_CA_TIMESTAMP = 'X-Ca-Timestamp'
# 请求放重放Nonce,15分钟内保持唯一,建议使用UUID
X_CA_NONCE = 'X-Ca-Nonce'
# APP KEY
X_CA_KEY = 'X-Ca-Key'
# Ali API Stage
X_CA_STAGE = 'X-Ca-Stage'
# API 版本号，目前所有 API 仅支持版本号『1』，可以不设置此请求头，默认版本号为『1』。
X_CA_VERSION = 'X-Ca-Version'
# 是否开启 Debug 模式，大小写不敏感，不设置默认关闭，一般 API 调试阶段可以打开此设置。
X_CA_REQUEST_MODE = 'X-Ca-Request-Mode'
# our token
X_TOKEN = 'token'

HOST_NAME = 'https://api.ginkgoo.cn'
# 默认请求超时时间,单位毫秒
DEFAULT_TIMEOUT = 1000
# Accept
ACCEPT_ALL = '*/*'
ACCEPT_JSON = 'application/json'
# Content-Type
CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded; charset=UTF-8'
CONTENT_TYPE_FORM = 'multipart/form-data; boundary=FullJump_Straight_App'
CONTENT_TYPE_JSON = 'application/json; charset=utf-8'
# 阿里API网关App Key
ALI_API_APP_KEY = '24730524'
ALI_API_SECRET = 'df8c84349d8dc25a5088b1b5b415a26f'

ignore_headers = [
    X_CA_SIGNATURE,
    X_CA_SIGNATURE_HEADERS,
    HTTP_HEADER_ACCEPT,
    HTTP_HEADER_CONTENT_MD5,
    HTTP_HEADER_CONTENT_TYPE,
    HTTP_HEADER_DATE
]

x_token = ''


def build_headers(headers):
    string_headers = ''
    for item in headers:
        if len(string_headers) != 0:
            string_headers += LF
        string_headers += item[0]
        if item[1] is not None:
            string_headers += ':{0}'.format(item[1])
    return string_headers


def build_url(path, params):
    if params is None:
        return path

    query = ''
    for k, v in params.items():
        if len(query) == 0:
            query += SPE5
        else:
            query += SPE3
        query += '{0}'.format(k)
        if v is not None and len(v) != 0:
            query += '{0}{1}'.format(SPE4, v)
    return path + query


def build_signature_headers(headers):
    sign_headers = ''
    for item in headers:
        if len(sign_headers) != 0:
            sign_headers += SPE1
        sign_headers += item[0]

    return sign_headers


def sign(secret, path, method, headers, params, date):
    sign_headers = []
    header_keys = sorted(headers.keys())
    for key in header_keys:
        try:
            ignore_headers.index(key)
            continue
        except ValueError:
            sign_headers.append((key, headers[key]))

    sign_string1 = method.upper()
    sign_string2 = LF + \
        headers[HTTP_HEADER_ACCEPT] \
        if HTTP_HEADER_ACCEPT in headers and \
        headers[HTTP_HEADER_ACCEPT] is not None \
        else LF
    sign_string3 = LF + \
        headers[HTTP_HEADER_CONTENT_MD5] \
        if HTTP_HEADER_CONTENT_MD5 in headers and \
        headers[HTTP_HEADER_CONTENT_MD5] is not None \
        else LF
    sign_string4 = LF + \
        headers[HTTP_HEADER_CONTENT_TYPE] \
        if HTTP_HEADER_CONTENT_TYPE in headers and \
        headers[HTTP_HEADER_CONTENT_TYPE] is not None \
        else LF
    sign_string5 = LF
    sign_string6 = LF + build_headers(sign_headers)
    sign_string7 = LF + build_url(path, params)
    sign_string = ''.join(
        [sign_string1, sign_string2, sign_string3, sign_string4, sign_string5, sign_string6, sign_string7]
    )
    print(sign_string)
    hash_sign_bytes = hmac.new(secret.encode(), sign_string.encode(), digestmod=hashlib.sha256).digest()
    hash_sign_string = base64.b64encode(hash_sign_bytes)
    return {
        'signature': hash_sign_string.decode('utf-8'),
        'signatureHeaders': build_signature_headers(sign_headers)
    }


def check_status(response):
    pass


def common_headers(date):
    headers = {
        X_CA_KEY: ALI_API_APP_KEY,
        X_CA_TIMESTAMP: str(int(date.timestamp() * 1000)),
        X_CA_NONCE: str(uuid.uuid1()),
        HTTP_HEADER_ACCEPT: ACCEPT_ALL,
        HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_FORM_URLENCODED,
        # X_CA_STAGE: 'TEST',
        # X_CA_STAGE: 'PRE',
        X_CA_STAGE: 'RELEASE',
        X_CA_REQUEST_MODE: 'DEBUG'
    }
    return headers


def build_body(params):
    form_data = []
    for k, v in params.items():
        if v is not None:
            form_data.append('{0}{1}{2}'.format(k, SPE4, v))
        else:
            form_data.append('{0}'.format(k))

    return SPE3.join(form_data)


def build_query(params):
    query = ''
    if params is None:
        return query

    param_keys = params.keys().sort()
    for key in param_keys:
        if len(query) == 0:
            query += SPE5
        else:
            query += SPE3

        if params[key] is not None:
            query += '{0}={1}'.format(key, params[key])
        else:
            query += '{0}'.format(key)

    return query


def prepare_options(request_date, headers, params):
    default_headers = common_headers(request_date)
    headers.update(default_headers)
    params[X_TOKEN] = x_token


def sign_request(path, method, headers, params, request_date):
    sign_result = sign(ALI_API_SECRET, path, method, headers, params, request_date)
    headers[X_CA_SIGNATURE] = sign_result['signature']
    headers[X_CA_SIGNATURE_HEADERS] = sign_result['signatureHeaders']


def request(path, method='GET', headers=None, params=None, credentials='omit'):
    if headers is None:
        headers = {}
    if params is None:
        params = {}

    request_date = datetime.datetime.now()
    url = HOST_NAME + path
    prepare_options(request_date, headers, params)
    sign_request(path, method, headers, params, request_date)

    body = None
    if method == 'POST' or method == 'PUT':
        body = build_body(params)
    else:
        url += build_query(params)

    try:
        r = None
        if method == 'POST':
            r = requests.post(url, headers=headers, data=body)
        if r.status_code != 200:
            for k, v in r.headers.items():
                print('{0}: {1}'.format(k, v))
            return ''
        else:
            return r.json()
    except Exception as e:
        print(e)
        return ''


if __name__ == '__main__':
    m = hashlib.md5()
    m.update(b'admin')
    result_json = request(path='/fjadmin/login',
                          method='POST',
                          params={
                              'name': 'admin',
                              'password': m.hexdigest()
                          })
    print(result_json)
